#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
// #define THREAD_SAFE_QUEUE
// #define THREAD_SAFE_QUEUE_ONE_P_ONE_C
#define THREAD_SAFE_QUEUE_ALL_P_ALL_C
#ifdef THREAD_SAFE_QUEUE_ALL_P_ALL_C
std::condition_variable cv_empty;
std::condition_variable cv_full;
std::mutex _mtx;
#endif
template <typename T>
class ThreadSafeQueue
{
private:
    mutable std::mutex mtx;
    std::queue<T> queue;
#ifdef THREAD_SAFE_QUEUE
    std::condition_variable cv;
#endif
#ifdef THREAD_SAFE_QUEUE_ONE_P_ONE_C
    std::condition_variable cv_not_empty;
    std::condition_variable cv_not_full;
#endif
    int max_capacity = 1;

public:
    ThreadSafeQueue() {}
    ThreadSafeQueue(int capacity) : max_capacity(capacity) {}
    void push(T value)
    {
#ifdef THREAD_SAFE_QUEUE
        std::lock_guard<std::mutex> lock(mtx);
        queue.push(std::move(value));
        cv.notify_one();
        std::this_thread::sleep_for(std::chrono::milliseconds(20));
#endif
#if defined(THREAD_SAFE_QUEUE_ONE_P_ONE_C) || defined(THREAD_SAFE_QUEUE_ALL_P_ALL_C)
#ifdef THREAD_SAFE_QUEUE_ONE_P_ONE_C
        std::unique_lock<std::mutex> lock(mtx);
        cv_not_full.wait(lock, [&]
                         { return queue.size() < max_capacity; }); // 非满条件变量等队列变空
        queue.push(std::move(value));
        cv_not_empty.notify_one(); // 非空条件变量通知消费者
#endif
#ifdef THREAD_SAFE_QUEUE_ALL_P_ALL_C
        queue.push(std::move(value));
#endif
        std::this_thread::sleep_for(std::chrono::milliseconds(50));
#endif
    }
    T pop()
    {
#ifdef THREAD_SAFE_QUEUE
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, [&]
                { return !queue.empty(); });
        T value = std::move(queue.front());
        queue.pop();
        std::this_thread::sleep_for(std::chrono::milliseconds(20));
        return value;
#endif
#if defined(THREAD_SAFE_QUEUE_ONE_P_ONE_C) || defined(THREAD_SAFE_QUEUE_ALL_P_ALL_C)
#ifdef THREAD_SAFE_QUEUE_ONE_P_ONE_C
        std::unique_lock<std::mutex> lock(mtx);
        cv_not_empty.wait(lock, [&]
                          { return !queue.empty(); });
        T value = std::move(queue.front());
        queue.pop();
        cv_not_full.notify_one();
#endif
#ifdef THREAD_SAFE_QUEUE_ALL_P_ALL_C
        T value = std::move(queue.front());
        queue.pop();
#endif
        std::this_thread::sleep_for(std::chrono::milliseconds(50));
        return value;
#endif
    }
    bool try_pop(T &value)
    {
        std::lock_guard<std::mutex> lock(mtx);
        if (queue.empty())
            return false;
        value = std::move(queue.front());
        queue.pop();
        return true;
    }
    bool empty() const
    {
        std::lock_guard<std::mutex> lock(mtx);
        return queue.empty();
    }
    int capacity() const
    {
        std::lock_guard<std::mutex> lock(mtx);
        return max_capacity;
    }
    int size() const
    {
        std::lock_guard<std::mutex> lock(mtx);
        return queue.size();
    }
};
#ifdef THREAD_SAFE_QUEUE_ONE_P_ONE_C
void producer(ThreadSafeQueue<std::string> &queue)
{
    for (int i = 0; i < 10; i++)
    {
        std::string message = "Message " + std::to_string(i);
        queue.push(message);
        std::cout << "Produced: " << message << std::endl;
    }
}
void consumer(ThreadSafeQueue<std::string> &queue)
{
    while (true)
    {
        std::string message = queue.pop();
        std::cout << "Consumed: " << message << std::endl;
        if (message == "Message 9")
            break;
    }
}
#endif
#ifdef THREAD_SAFE_QUEUE_ALL_P_ALL_C
void producer(ThreadSafeQueue<std::string> &queue)
{
    std::unique_lock<std::mutex> lock(_mtx);
    cv_empty.wait(lock, [&]
                  { return queue.empty(); });
    for (int i = 0; i < queue.capacity(); i++)
    {
        std::string message = "Message " + std::to_string(i % 10);
        queue.push(message);
        std::cout << "Produced: " << message << std::endl;
    }
    cv_full.notify_one();
}
void consumer(ThreadSafeQueue<std::string> &queue)
{
    std::unique_lock<std::mutex> lock(_mtx);
    cv_full.wait(lock, [&]
                 { return queue.capacity() == queue.size(); });
    for (int i = 0; i < queue.capacity(); i++)
    {
        std::string message = queue.pop();
        std::cout << "Consumed: " << message << std::endl;
        if (message == "Message 9")
            break;
    }
    cv_empty.notify_one();
}
#endif
int main()
{
#ifdef THREAD_SAFE_QUEUE_ONE_P_ONE_C
    ThreadSafeQueue<std::string> queue;
#endif
#ifdef THREAD_SAFE_QUEUE_ALL_P_ALL_C
    ThreadSafeQueue<std::string> queue(10);
#endif
    std::thread producerThread(producer, std::ref(queue));
    std::thread consumerThread(consumer, std::ref(queue));
    // std::ref(obj) 就是告诉标准库：“别拷贝，请把 obj 按引用传进去！
    producerThread.join();
    consumerThread.join();
    return 0;
}